In this document we learn how to create interactive charts with leaflet. Simply put, we are learning how to transform tidy data into visually clear graphs. In the overall context of the workflow, this falls into the category of transforming our data into data visualisation.
{{<expand "Note: LinkedIn Learning videos" "...">}} There are references to LinkedIn Learning videos. These are complementary but not really required as the notes below are meant to be self-contained. Some students and staff would have access for free. Do not purchase access unless you are sure you don’t have access through your organisation already. {{</expand>}}
library("leaflet")
%>%) operator to create chartsThe example data used is from the ACORN-SAT dataset. The dataset spans 112 Australian stations recording temperature measurements for over 100 years. We will use data from the year 2000.
station_data %>%
sample_n(5)
## Number year average.temp Station.name Latitude Longitude Elevation Start
## 1 14015 2000 27.3 Darwin -12.42 130.89 30 1910
## 2 53115 2000 19.3 Moree -29.49 149.85 213 1912
## 3 94029 2000 13.6 Hobart -42.89 147.33 50 1918
## 4 15135 2000 24.8 Tennant Creek -19.64 134.18 375 1910
## 5 33119 2000 22.4 Mackay -21.12 149.22 30 1910
leaflet() function to begin any leaflet chartstation_data %>%
leaflet()
addTiles() to get a default mapplot <- station_data %>%
leaflet() %>%
addTiles()
widgetframe::frameWidget(plot)
Notice how we can move around and zoom in – Leaflet is very interactive!
addProviderTiles() to use a custom mapplot <- station_data %>%
leaflet() %>%
addProviderTiles(providers$Esri.NatGeoWorldMap)
widgetframe::frameWidget(plot)
addMarkers() to place our geographical datastation_data %>%
leaflet() %>%
addTiles() %>%
addMarkers(lng = ~Longitude,
lat = ~Latitude)
If we have numeric latitude and longitude columns in our data, addMarkers() will automatically use these. If your columns aren’t automatically detected, try using lat = ~as.numeric(Latitude). The error may be caused by numeric data stored as a character variable!
label argument of addMarkers()popup argument of addMarkets()paste0() or paste()
station_data %>%
leaflet() %>%
addTiles() %>%
addMarkers(label = ~Station.name,
popup = ~paste0("Altitude: ",
Elevation,
"<br/>",
"<br/>",
"Isn't Leaflet neat?"),
lng = ~Longitude,
lat = ~Latitude)
Click on the markers!
Notice in the previous section, our arguments used a tilde (“~”) before we called on our column names. This even occured when we called them within a formula.
station_data %>%
leaflet() %>%
addTiles() %>%
addMarkers(label = ~Station.name,
popup = ~paste0("Altitude: ", Elevation),
lng = ~Longitude,
lat = ~Latitude)
We must do this, as this is the format Leaflet demands. It’s how Leaflet detects column names and converts them to lists of variables.
addCircleMarkers() instead of just addMarkers()Radius changes the size of circle plots according to a variable. Here we’d like the size to represent temperature.
station_data %>%
leaflet() %>%
addTiles() %>%
addCircleMarkers(label = ~paste("The", Station.name, "circle!"),
radius = ~average.temp,
lng = ~Longitude,
lat = ~Latitude)
We can also change the colour of the circle markers according to a variable, although this requires some setup.
colorNumeric() functionpalette argument specifies the colours we move on a gradient betweendomain argument is the list of numeric values we map to the gradientpal <- colorNumeric(
palette = "YlOrRd",
domain = station_data$average.temp)
color argument of addCircleMarkers()station_data %>%
leaflet() %>%
addTiles() %>%
addCircleMarkers(label = ~paste("The", Station.name, "circle!"),
color = ~pal(average.temp),
lng = ~Longitude,
lat = ~Latitude)
colorNumeric()colorRampPalette() function, it is possible to create custom palettespalette argument of colorNumeric() is defined using this special colorRampPalette() callpal <- colorNumeric(
palette = colorRampPalette(c("green", "purple"))(length(station_data$average.temp)),
domain = station_data$average.temp)
A choropleth is a map-based chart in which regions are shaded with colours to reflect some variable. Creating choropleths with Leaflet requires us to manipulate ‘shapefiles’. These are files which contain information about points, lines, polygons (etc) necessary to visually depict shapes, such as countries of the world. Before we can create a choropleth, we must learn how to prepare these shape files
There are two types of shapefiles - ESRI shapefiles - the older standard for shapefiles - To use them we must have (at least) one of all of the below: - A .dbf file - A .shp file - A .shx file - GeoJson shapefiles - a newer type - To use them we only require one .json file
A good sources of global shapefiles are NaturalEarthData.com and Johan’s repository
sf”read_sf() to read an entire directory of shapefiles and save the resultlibrary("sf")
## Linking to GEOS 3.8.1, GDAL 3.1.4, PROJ 6.3.1
shapefile_map <- read_sf(dsn = "shapefiles")
# Note: for file path, do not include a '/' at the end
shapefile_map
## Simple feature collection with 8 features and 2 fields
## geometry type: MULTIPOLYGON
## dimension: XY
## bbox: xmin: 112.9211 ymin: -43.74037 xmax: 159.1053 ymax: -9.142319
## CRS: NA
## # A tibble: 8 x 3
## STE COUNT geometry
## <int> <dbl> <MULTIPOLYGON>
## 1 1 11619 (((151.0689 -33.82025, 151.0697 -33.81847, 151.0705 -33.82025, 15…
## 2 2 7889 (((146.3024 -39.15425, 146.3018 -39.15395, 146.3024 -39.1532, 146…
## 3 3 6373 (((153.3613 -27.63769, 153.3603 -27.63577, 153.3635 -27.63518, 15…
## 4 4 3152 (((137.5385 -34.01007, 137.5391 -34.01418, 137.5375 -34.01343, 13…
## 5 5 3481 (((115.2278 -33.59031, 115.2281 -33.59122, 115.2291 -33.59122, 11…
## 6 6 1089 (((146.5695 -41.17678, 146.5697 -41.17737, 146.5691 -41.17769, 14…
## 7 7 389 (((130.8684 -12.35691, 130.8692 -12.35513, 130.8699 -12.35691, 13…
## 8 8 492 (((149.2214 -35.34117, 149.2198 -35.34141, 149.2189 -35.34153, 14…
shapefile_map variable has a column called “geometry”
addPolygons() Leaflet function to view our mapshapefile_map %>%
leaflet() %>%
addPolygons()
We have our shapes - we will now mutate our shape data so that they are named by state.
shapefile_map$State <- c("NSW", "VIC", "QLD", "SA", "WA", "TAS", "NT", "ACT")
What colours do we use?
How do we create our palette?
-Several functions exist - colorNumeric() for continuous numeric variable colouring - colorQuantile() for colouring by quantile of a numeric variable - colorBin() for colouring by bins of a numeric variable - colorFactor() for colouring by a categorical variable
Right now we have a map but no data. Let us colour by “State”, a categorical variable.
palette <- colorFactor("Set1", domain = shapefile_map$State)
shapefile_map %>%
leaflet() %>%
addTiles() %>%
addPolygons(color = ~palette(State)) %>%
addLegend(pal = palette, values = ~State, title = "State: ")
Here we have introduced the Leaflet addLegend() function - The pal argument specifies the palette we use for the legend - The values argument specifies the variable our legend explains - The title argument titles the legend
Let us add some actual data to our choropleth. We will use the 2013-2014 subset of water usage data from the Australian Environmental-Economic Accounts (2016)
building_state_data.Rload("tidy_EnvAcc_data/water_data.rdata")
water_data
## State DistributedReuseSupply(GL) Consumption(GL) Expenditure($m)
## 1 ACT 49 53 288
## 2 NSW 6211 7508 4192
## 3 NT 59 167 157
## 4 QLD 2579 4145 3877
## 5 SA 380 1077 1162
## 6 TAS 99 390 245
## 7 VIC 3238 3988 4554
## 8 WA 643 1317 1597
## HouseholdPhysical(GL) HouseholdMonetary($m) HouseholdPrice($/KL)
## 1 31 93 3.00
## 2 565 1589 2.81
## 3 37 60 1.62
## 4 370 1109 3.00
## 5 125 531 4.25
## 6 38 118 3.11
## 7 366 1240 3.39
## 8 339 557 1.64
water_data_map <- shapefile_map %>%
merge(water_data, by = "State")
We may now make a new palette and colour it. Let us do this by the average price of water.
palette <- colorNumeric("Blues", domain = water_data_map$`HouseholdPrice($/KL)`)
water_data_map %>%
leaflet() %>%
addTiles() %>%
addPolygons(color = ~palette(water_data_map$`HouseholdPrice($/KL)`),
label = ~paste("Price:",
water_data_map$`HouseholdPrice($/KL)`),
popup = ~paste("Average household consumption (KL):",
water_data_map$`HouseholdPhysical(GL)`,
"<br/>",
"Household expenditure ($m):",
water_data_map$`HouseholdMonetary($m)`)) %>%
addLegend(pal = palette,
values = ~water_data_map$`HouseholdPrice($/KL)`,
title = "Average price of water ($/KL): ")
Just like with addMarkers(), the label and popup arguments still work!
gcIntermediate() functionTo begin, we need the “geosphere” and “sp” packages. We also need two data.frames, each containing only a set of latitudes and longitudes. For our example, we filter out only the most eastern and most western coordinates.
library("geosphere")
library("sp")
load("tidy_ACORN-SAT_data/station_data.rdata")
east_to_west <- station_data %>%
filter(year == 2000)
start_loc <- east_to_west %>%
filter(Longitude == max(Longitude)) %>%
select(Longitude, Latitude)
end_loc <- east_to_west %>%
filter(Longitude == min(Longitude)) %>%
select(Longitude, Latitude)
gcIntermediate() to create our lines
SpatialLinesDataFrame() specifying our data to create one integrated objectst_as_sf() to format the object as an sfdistance <- gcIntermediate(p1 = start_loc,
p2 = end_loc,
n = 50,
addStartEnd = TRUE,
sp = TRUE) %>%
SpatialLinesDataFrame(data = east_to_west) %>%
st_as_sf()
addPolylines() specifying our object to plot the lineseast_to_west %>%
leaflet() %>%
addTiles() %>%
addCircleMarkers(label = ~Station.name) %>%
addPolylines(data = distance)